package main

/****
功能：
此文件处理上传时视频长 宽取值的问题
编译方式：
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ffmpeg_server index.go
运行方式：
./ffmpeg_server start
调用方式：
http://127.0.0.1:8000/?p=[图片/视频路径]
****/

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"syscall"

	"github.com/medivh-jay/daemon"
	"github.com/spf13/cobra"
)

var webPath string
var port string
var defaultCmd string

// HTTPServer http 服务器示例
type HTTPServer struct {
	http *http.Server
	cmd  *cobra.Command
}

// PidSavePath pid保存路径
func (httpServer *HTTPServer) PidSavePath() string {
	return "./"
}

// Name pid文件名
func (httpServer *HTTPServer) Name() string {
	return "http"
}

// SetCommand 从 daemon 获得 cobra.Command 对象
func (httpServer *HTTPServer) SetCommand(cmd *cobra.Command) {
	// 在这里添加参数时他的参数不是对应服务的 start stop restart 命令的, 比如这个示例服务
	// 他对应的是示例服务命令, s所以这里添加的自定义 flag 应该在 start 之前传入
	cmd.PersistentFlags().StringP("test", "t", "yes", "")
	cmd.PersistentFlags().StringP("path", "f", "/dnmp/www/server/public/", "默认为/dnmp/www/server/public/")
	cmd.PersistentFlags().StringP("port", "p", "8000", "默认为8000")
	cmd.PersistentFlags().StringP("cmd", "c", "", "默认执行的命令")
	httpServer.cmd = cmd
}

// Start 启动web服务
func (httpServer *HTTPServer) Start() {
	fmt.Println(httpServer.cmd.Flags().GetString("test"))
	port, _ = httpServer.cmd.Flags().GetString("port")
	defaultCmd, _ = httpServer.cmd.Flags().GetString("cmd")
	webPath, _ = httpServer.cmd.Flags().GetString("path")
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		// fmt.Println("hello world")
		// _, _ = writer.Write([]byte("hello world"))
		// w.Write([]byte("this is version 3"))
		r.ParseForm()
		// fmt.Println(r.Form)
		// fmt.Println("path", r.URL.Path)
		// fmt.Println("scheme", r.URL.Scheme)
		p, isOk := r.Form["p"]
		if !isOk {
			if defaultCmd != "" {
				// exe
				command := NewLinuxCommand()
				_, out, err := command.Exec(defaultCmd)
				// fmt.Println(pid, out, err)
				if err != nil {
					fmt.Fprintf(w, "<html><body><div>cmd:%s</div><div style='color:red;'>out:%s%s</div></body></html>", defaultCmd, out, err)
				} else {
					fmt.Fprintf(w, "<html><body><div>cmd:%s</div><div>out:%s</div></body></html>", defaultCmd, out)
				}
			} else {
				fmt.Fprintf(w, "Hello world!"+port+webPath+defaultCmd)
			}
			return
		}
		path := webPath + p[0]
		if !IsExist(path) {
			fmt.Fprintf(w, "dir error:%s", path)
			return
		}
		command := NewLinuxCommand()
		_, out, err := command.Exec("ffmpeg -i " + path)
		// fmt.Println(pid, out, err)
		fmt.Fprintf(w, "path:%s \r\n:%s%s", path, out, err)
	})
	httpServer.http = &http.Server{Handler: http.DefaultServeMux, Addr: ":" + port}
	_ = httpServer.http.ListenAndServe()
}

// Stop 关闭web服务
func (httpServer *HTTPServer) Stop() error {
	fmt.Println("准备关闭服务器")
	err := httpServer.http.Shutdown(context.Background())
	fmt.Println("服务器已经关闭")
	return err
}

// Restart 重启web服务前关闭http服务
func (httpServer *HTTPServer) Restart() error {
	fmt.Println("服务器关闭中")
	err := httpServer.Stop()
	return err
}

func main() {
	// 自定义输出文件
	out, _ := os.OpenFile("./http.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
	err, _ := os.OpenFile("./http_err.log", os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
	port = "80"
	// flag.StringVar(&webPath, "path", "/data/wwwroot/", "默认为/data/wwwroot/")
	// flag.StringVar(&port, "port", "8080", "默认为8080")
	// flag.StringVar(&defaultCmd, "cmd", "", "默认执行的命令")
	// // 解析命令行参数写入注册的flag里
	// flag.Parse()
	// fmt.Println("Starting Http Server " + port + " path:" + webPath + " default cmd:" + defaultCmd)
	// 初始化一个新的运行程序
	proc := daemon.NewProcess(new(HTTPServer)).SetPipeline(nil, out, err)
	proc.On(syscall.SIGTERM, func() {
		fmt.Println("a custom signal")
	})
	// 示例,多级命令服务
	// 这里的示例由于实现了 Command 接口, 所以这里会出现 flag test 不存在的情况, 实际情况, 每一个 worker 都应该是唯一的
	// 不要共享一个 worker 对象指针
	daemon.GetCommand().AddWorker(proc).AddWorker(proc)
	// 示例,主服务
	daemon.Register(proc)

	// 运行
	if rs := daemon.Run(); rs != nil {
		log.Fatalln(rs)
	}
}

// package main

// import (
// 	"errors"
// 	"flag"
// 	"fmt"
// 	"io/ioutil"
// 	"log"
// 	"net/http"
// 	"os"
// 	"os/exec"
// 	"os/signal"
// 	"runtime"
// 	"syscall"
// 	"time"
// )

// // 主动关闭服务器
// var server *http.Server

// var webPath string
// var port string

// func main() {
// 	// 定义几个变量，用于接收命令行的参数值
// 	// &user 就是接收命令行中输入 -u 后面的参数值，其他同理
// 	flag.StringVar(&webPath, "path", "/data/wwwroot/", "默认为/data/wwwroot/")
// 	flag.StringVar(&port, "port", "8080", "默认为8080")
// 	// 解析命令行参数写入注册的flag里
// 	flag.Parse()

// 	// 一个通知退出的chan
// 	quit := make(chan os.Signal)
// 	signal.Notify(quit, os.Interrupt)

// 	mux := http.NewServeMux()
// 	mux.Handle("/", &myHandler{})
// 	// mux.HandleFunc("/bye", sayBye)

// 	server = &http.Server{
// 		Addr:         ":" + port,
// 		WriteTimeout: time.Second * 10,
// 		Handler:      mux,
// 	}

// 	go func() {
// 		// 接收退出信号
// 		<-quit
// 		if err := server.Close(); err != nil {
// 			log.Fatal("Close server:", err)
// 		}
// 	}()

// 	log.Println("Starting httpserver")
// 	err := server.ListenAndServe()
// 	if err != nil {
// 		// 正常退出
// 		if err == http.ErrServerClosed {
// 			log.Fatal("Server closed under request")
// 		} else {
// 			log.Fatal("Server closed unexpected", err)
// 		}
// 	}
// 	log.Fatal("Server exited")

// }

// type myHandler struct{}

// func (*myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 	// w.Write([]byte("this is version 3"))
// 	r.ParseForm()
// 	// fmt.Println(r.Form)
// 	// fmt.Println("path", r.URL.Path)
// 	// fmt.Println("scheme", r.URL.Scheme)
// 	p, isOk := r.Form["p"]
// 	if !isOk {
// 		fmt.Fprintf(w, "Hello world!")
// 		return
// 	}
// 	path := webPath + p[0]
// 	if !IsExist(path) {
// 		fmt.Fprintf(w, "dir error:%s", path)
// 		return
// 	}
// 	command := NewLinuxCommand()
// 	_, out, err := command.Exec("cd " + path + " && git pull")
// 	// fmt.Println(pid, out, err)
// 	fmt.Fprintf(w, "<html><body><div>path:%s </div><div>out:%s%s</div></body></html>", path, out, err)
// }

// // 关闭http
// func sayBye(w http.ResponseWriter, r *http.Request) {
// 	w.Write([]byte("bye bye ,shutdown the server")) // 没有输出
// 	err := server.Shutdown(nil)
// 	if err != nil {
// 		log.Fatal([]byte("shutdown the server err"))
// 	}
// }
func IsExist(f string) bool {
	_, err := os.Stat(f)
	return err == nil || os.IsExist(err)
}

// LinuxCommand结构体
type LinuxCommand struct {
}

// LinuxCommand的初始化函数
func NewLinuxCommand() *LinuxCommand {
	return &LinuxCommand{}
}

// 执行命令行并返回结果
// args: 命令行参数
// return: 进程的pid, 命令行结果, 错误消息
func (lc *LinuxCommand) Exec(args ...string) (int, string, error) {
	args = append([]string{"-c"}, args...)
	cmd := exec.Command(os.Getenv("SHELL"), args...)

	cmd.SysProcAttr = &syscall.SysProcAttr{}

	outpip, err := cmd.StdoutPipe()

	defer outpip.Close()
	outpiperr, err := cmd.StderrPipe()
	defer outpiperr.Close()

	if err != nil {
		return 0, "", err
	}

	err = cmd.Start()
	if err != nil {
		return 0, "", err
	}

	out, err := ioutil.ReadAll(outpip)
	if err != nil {
		return 0, "", err
	}
	outerr, err := ioutil.ReadAll(outpiperr)
	if err != nil {
		return 0, "", err
	}
	if string(outerr) != "" {
		return 0, "", errors.New(string(outerr))
	}
	return cmd.Process.Pid, string(out), nil
}

// 异步执行命令行并通过channel返回结果
// stdout: chan结果
// args: 命令行参数
// return: 进程的pid
// exception: 协程内的命令行发生错误时,会panic异常
func (lc *LinuxCommand) ExecAsync(stdout chan string, args ...string) int {
	var pidChan = make(chan int, 1)

	go func() {
		args = append([]string{"-c"}, args...)
		cmd := exec.Command(os.Getenv("SHELL"), args...)

		cmd.SysProcAttr = &syscall.SysProcAttr{}

		outpip, err := cmd.StdoutPipe()
		defer outpip.Close()

		if err != nil {
			panic(err)
		}

		err = cmd.Start()
		if err != nil {
			panic(err)
		}

		pidChan <- cmd.Process.Pid

		out, err := ioutil.ReadAll(outpip)
		if err != nil {
			panic(err)
		}

		stdout <- string(out)
	}()

	return <-pidChan
}

// 执行命令行(忽略返回值)
// args: 命令行参数
// return: 错误消息
func (lc *LinuxCommand) ExecIgnoreResult(args ...string) error {

	args = append([]string{"-c"}, args...)
	cmd := exec.Command(os.Getenv("SHELL"), args...)

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{}

	err := cmd.Run()

	return err
}
